<?php
namespace Anker1992\JWTAuth\Component;

use Firebase\JWT\JWT;
use \DomainException;
use \UnexpectedValueException;
use \InvalidArgumentException;
use Anker1992\JWTAuth\JWTAuthCode;
use Anker1992\JWTAuth\JWTAuthException;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;


class Auth {
    protected $jwtSecret; //秘钥
    protected $userlimit; //token有效期，单位s
    protected $refreshLimit; //令牌刷新有效期，单位s

    /**
     * 初始化
     */
    public function __construct($config) {
        $this->jwtSecret = isset($config['jwtSecret']) && !empty($config['jwtSecret']) ? $config['jwtSecret'] : 'test';
        $this->userlimit = isset($config['userlimit']) && !empty($config['userlimit']) ? $config['userlimit'] : 3600; //默认一小时
        $this->refreshLimit = isset($config['refreshLimit']) && !empty($config['refreshLimit']) ? $config['refreshLimit'] : 604800; //默认七天
    }
    
    /**
     * 获取token
     * @param $data
     * @return string
     * @throws JWTAuthException
     */
    public function getToken($data) {
        $invaliTime = time();
        $expireTime = time() + $this->refreshLimit;
        $keyId = Tool::random();
        $payload = [
            'nbf' => $invaliTime,
            'exp' => $expireTime,
            'jwt_ide' => $keyId,
            'data' => $data
        ];
        try {
            return JWT::encode($payload, $this->jwtSecret, 'HS256', $keyId);
        } catch(DomainException $e) {
            throw new JWTAuthException('数据加密出错', JWTAuthCode::ENCRYPT_EORROR);
        }
    }

    /**
     * 验证tonken有效性，不正确抛出异常，正确返回用户数据
     * @param string $token
     * @return array
     * @throws JWTAuthException
     */
    public function check($token = '') {
        $token_obj = $this->analysisToken($token);
        if (time() - $token_obj->nbf >= $this->userlimit) {
            throw new JWTAuthException('token过期需要刷新',JWTAuthCode::TOKEN_EXPIRE);
        }
        return Tool::object_to_array($token_obj);
    }

    /**
     * 刷新token
     * @param string $token
     * @return string
     * @throws JWTAuthException
     */
    public function refreshToken($token = '') {
        $token_obj = $this->analysisToken($token);
        $this->_addBlacklist($token_obj->jwt_ide);
        return $this->getToken($token_obj->data);
    }

    /**
     * 注销token
     * @param string $token
     * @return bool
     * @throws JWTAuthException
     */
    public function killToken($token){
        $token_obj = $this->analysisToken($token);
        $this->_addBlacklist($token_obj->jwt_ide);
        return true;
    }

    /**
     * 解析token
     * @param string $token
     * @return object
     * @throws JWTAuthException
     */
    public function analysisToken($token) {
        try {
            $token_obj = JWT::decode($token, $this->jwtSecret, ['HS256']);
        }catch (InvalidArgumentException $e){
            throw new JWTAuthException('未设置jwt秘钥',JWTAuthCode::JWT_SECRET_MISS);
        }catch (UnexpectedValueException $e){
            throw new JWTAuthException('token格式异常：'.$e->getMessage(),JWTAuthCode::INVALID_TOKEN);
        }catch (SignatureInvalidException $e){
            throw new JWTAuthException('token格式异常：'.$e->getMessage(),JWTAuthCode::INVALID_TOKEN);
        }catch (DomainException $e){
            throw new JWTAuthException('token格式异常：'.$e->getMessage(),JWTAuthCode::INVALID_TOKEN);
        }catch (BeforeValidException $e){
            throw new JWTAuthException('token失效：'.$e->getMessage(),JWTAuthCode::INVALID_TOKEN);
        }catch (ExpiredException $e){
            throw new JWTAuthException('token完全失效：'.$e->getMessage(),JWTAuthCode::TOKEN_EXPIRE_LONG);
        }
        if($this->_inBlacklist($token_obj->jwt_ide) === true ){
            throw new JWTAuthException('token已被注销',JWTAuthCode::TOKEN_LOGOUT);
        }
        return $token_obj;
    }

    /**
     * 验证token是否在黑名单（注销的token会加入黑名单）
     * @param $jwt_ide token标识
     * @return bool
     */
    protected function _inBlacklist($jwt_ide) {
        $key = 'jwt_ide_'. $jwt_ide;
        $cache = new FilesystemAdapter();
        $valueProducts = $cache->getItem($key);
        if (!$valueProducts->isHit()) {
            return false;
        }
        return true;
    }

    /**
     * 将token加入黑名单（注销token）
     * @param $jwt_ide
     * @return bool
     */
    protected function _addBlacklist($jwt_ide){
        $key = 'jwt_ide_'.$jwt_ide;
        $cache = new FilesystemAdapter();
        $valueProducts = $cache->getItem($key);
        $valueProducts->set(1);
        $valueProducts->expiresAfter($this->refreshLimit);
        $cache->save($valueProducts);
        return true;
    }
}